# -*- coding: utf-8 -*-

import re
import sys
from renyare.wsgi import RenyareMiddleware
import renyare.util as util

class Combiner(RenyareMiddleware):
	"""
	A WSGI application, that does the following:
		- create XML document with base tags containing some information such as linkbase etc.
		- scan wsgi variables for 'renyare.applications' array and insert the
		  output of all applications in the document
		- Check for the unparsed text output from main application, detect it's type and
		  mark them main application depending of output existence
	"""
	def call(self):
		# Create the document and publish the content node
		# (If some applications want to use it)
		content = self.base_doc()
		#self.environ['renyare.content'] = content
		self.environ['renyare.require_parsed'] = True # We need parsed content here
		self.environ['renyare.page'] = {}
		self.environ['renyare.page']['info'] = self.infoNode
		self.environ['renyare.page']['content'] = content
		# Gather output from apllication
		self.output = ""
		for data in self.app(self.environ, self.xml_response):
			self.output += data
			yield "" # http://www.python.org/dev/peps/pep-0333/#id23

		# Process outputs from applications list
		main_node = None
		if(self.environ.has_key('renyare.applications')):
			for app in self.environ['renyare.applications']:
				# Process unparsed content
				unparsed = app.get('text')
				is_empty = True
				app_text = None
				if(unparsed): # Application has unparsed content
					# Prepare text for importing
					try:
						# !!! Weak place - parseString is not DOM2-compliant !!!
						app_text = dom.parseString(unparsed).documentElement
						app_text = self.xml.importNode(app_text, True)
					except:
						app_text = self.xml.createCDATASection(unparsed)
					app_text.tagName = 'plain'
					is_empty = False

				# Process parsed content
				if(app['node'] and app['node'].hasChildNodes()): # Application or middleware provided xml data
					if(not app['node'].ownerDocument == content.ownerDocument):
						app['node'] = self.xml.importNode(app['node'], True)
					is_empty = False
				# Application has parsed or unparsed content
				if(not is_empty):
					part = self.xml.createElement(nsp+'part')
					sName = app['name'].split('.', 2)
					part.setAttribute('name', app['name'])
					app['node'].tagName = sName[0]
					if(app.has_key('action')):
						part.setAttribute('action', app['action'])
					if(app['is_main']):
						part.setAttribute('main', '1')
					if(app_text):
						plain = self.xml.createElement(nsp+'plain')
						plain.appendChild(app_text)
					part.appendChild(app['node'])
					content.appendChild(part)
				else:
					if(app['node']):
						if(app['node'].parentNode):
							app['node'].parentNode.removeChild(app['node'])
					app['document'] = None
					app['node'] = None

		# Process the output of the main application if it exists
		if(self.output):
			if(self.app_ct == 'text'):
				main_node = content.appendChild(self.xml.createElement(nsp+'main'))
				main_node.appendChild(self.xml.createTextNode(self.output))
			elif(self.app_ct in ('xml', 'html')):
				try:
					# !!! Weak place - parseString is not DOM2-compliant !!!
					xml_output = dom.parseString(self.output)
					main_node = self.xml.createElement(nsp+'main')
					main_node.appendChild(xml_output.documentElement)
					main_node.setAttribute('output', self.app_ct)
				except:
					import cStringIO, traceback
					out=cStringIO.StringIO()
					traceback.print_exc(file=out)
					self.output = "Error parsing main application output: %s %s %s; \nTraceback: %s" % (str(self.app), self.output,  sys.exc_info()[1], out.getvalue())
					main_node = self.xml.createElementNS(ns_uri, nsp+'main')
					main_node.appendChild(self.xml.createTextNode(self.output))
			else:
				self.output = 'Unknown output type: %s' % self.app_ct
				main_node = self.xml.createElementNS(ns_uri, nsp+'main')
				main_node.appendChild(self.xml.createTextNode(self.output))
		if(main_node):
			main_node.setAttributeNS(ns_uri, 'main', '1')
			content.appendChild(main_node)
		if(not 'renyare.transform' in self.environ): # We have a transformer
			yield util.xmlToStr(self.xml)

	def detect_output_type(self, headers):
		new_headers = []
		for hdr in headers:
			if(hdr[0].lower() == 'content-type'):
				if(hdr[1].lower() == 'text/plain'):
					self.app_ct = 'text'
				elif(re.match('.*\/xml.*', hdr[1].lower())):
					self.app_ct = 'xml'
				elif(re.match('.*\/[x]?html', hdr[1].lower())):
					self.app_ct = 'html'
				else:
					self.app_ct = hdr[1].lower()

	def xml_response(self, status, headers, exc_info=None):
		# Detect application headers
		self.detect_output_type(headers)
		if(status[0:3]=='200'):
			new_status = "200 OK"
			xml_headers = [x for x in headers if not x[0].lower() == 'content-type'] # Remove all content-types
			xml_headers.append(('Content-type','application/xml'))
			return self.start_response(new_status, xml_headers, exc_info)
		else:
			return self.start_response(status, headers, exc_info)

	def base_doc(self):
		"""
		Creates initial xml nodes, that will appear in all documents
		"""
		# Create page information node
		self.infoNode = self.xml.createElement(nsp+'info')
		self.xml.documentElement.appendChild(self.infoNode)
		# Create content node
		content = self.xml.createElement( nsp+'content')
		self.xml.documentElement.appendChild(content)
		# Fill the information node
		base = self.infoNode.appendChild(self.xml.createElement(nsp+'base'))
		base.appendChild(self.xml.createTextNode(self.cf.get('paths', 'web')))
		content_info = self.infoNode.appendChild(self.xml.createElement(nsp+'content'))
		content_info.appendChild(self.xml.createTextNode('/page/content'))
		fullpath = self.infoNode.appendChild(self.xml.createElement(nsp+'relpath'))
		path = self.environ.get('PATH_INFO') or '' # self.environ.get('PATH_INFO') can be None
		fullpath.appendChild(self.xml.createTextNode(path.rstrip('/')))
		# Return content node
		return content
